/**
 * 
 */
package net.teamclerks.junit;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import net.teamclerks.rpgf.avatar.Avatar;
import net.teamclerks.rpgf.avatar.controller.AvatarController;
import net.teamclerks.rpgf.character.Character;
import net.teamclerks.rpgf.character.controller.CharacterController;
import net.teamclerks.rpgf.character.stats.Stat;
import net.teamclerks.rpgf.character.statsheet.StatusEffectStatSheet;
import net.teamclerks.rpgf.inventory.Inventory;
import net.teamclerks.rpgf.inventory.item.ConsumableItem;
import net.teamclerks.rpgf.inventory.item.Item;
import net.teamclerks.rpgf.inventory.item.equipment.ArmorItem;
import net.teamclerks.rpgf.inventory.item.equipment.WeaponItem;
import net.teamclerks.rpgf.scene.Scene;
import net.teamclerks.rpgf.script.director.Director;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

/**
 * @author msmith
 *
 */
public class RPGFTest
{
  private Director director;
  
  public RPGFTest(){}
  
  /**
   * Tests successful creation of the director.
   */
  @Test
  public void testCreateDirector()
  {
    if(this.director == null)
    {
      this.director = Director.getInstance();
    }
    Assert.assertNotNull(director);
  }
  
  /**
   * Tests creation of the AvatarController.<br/>
   * Tests whether the Director will accept the AvatarController.<br/>
   * Depends on testCreateDirector().
   */
  @Test
  public void testCreateAvatarController()
  {
    testCreateDirector();
    if(director.getAvatarController() == null)
    {
      new AvatarController(director);
    }
    Assert.assertNotNull(director.getAvatarController());
    if(director.getAvatarController().getAvatars() == null)
    {
      director.getAvatarController().setAvatars(new ArrayList<Avatar>());
    }
    Assert.assertNotNull(director.getAvatarController().getAvatars());
  }
  
  /**
   * Tests creating the CharacterController.<br/>
   * Tests the CharacterController will accept a list of party members and opponents.<br/>
   * Tests that the Director will accept this CharacterController.<br/>
   * Depends on testCreateDirect().
   */
  @Test
  public void testCreateCharacterController()
  {
    testCreateDirector();
    if(director.getCharacterController() == null)
    {
      new CharacterController(director);
    }
    Assert.assertNotNull(director.getCharacterController());
    if(director.getCharacterController().getOpponents() == null)
    {
      director.getCharacterController().setOpponents(new ArrayList<Character>());
    }
    Assert.assertNotNull(director.getCharacterController().getOpponents());
    if(director.getCharacterController().getPartyMembers() == null)
    {
      director.getCharacterController().setPartyMembers(new ArrayList<Character>());
    }
    Assert.assertNotNull(director.getCharacterController().getPartyMembers());
  }
  
  /**
   * Tests creating the Inventory.<br/>
   * Tests creating a list of Items for the Inventory.<br/>
   * Tests whether the Director will accept the Inventory.<br/>
   * Depends on testCreateDirector().
   */
  @Test
  public void testCreateInventory()
  {
    testCreateDirector();
    if(director.getInventory() == null)
    {
      new Inventory(director);
      director.getInventory().setItems(new ArrayList<Item>());
    }
    Assert.assertNotNull(director.getInventory());
  }
  
  /**
   * Tests creating a Scene.<br/>
   * Tests whether the Director will accept the Scene.<br/>
   * Depends on testCreateDirector().
   */
  @Test
  public void testCreateScene()
  {
    testCreateDirector();
    if(director.getScene() == null)
    {
      new Scene(director);
    }
    Assert.assertNotNull(director.getScene());
  }
  
  /**
   * Tests creating a ConsumableItem.
   * Tests using the same ConsumableItem.
   * Depends on testCreateDirector().
   */
  @Test
  public void testCreateItem()
  {
    testCreateDirector();
    ConsumableItem potion = new ConsumableItem(director);
    Assert.assertNotNull(potion);
    potion.setId(1);
    Assert.assertEquals(1,potion.getId());
    potion.setCount(1);
    Assert.assertEquals(1,potion.getCount());
    potion.setName("Potion");
    Assert.assertEquals("Potion", potion.getName());
    Assert.assertTrue(potion.isConsumable());
    Assert.assertTrue(potion.isUsable());
    // Create the Inventory
    if(director.getInventory() == null)
    {
      new Inventory(director);
      director.getInventory().setItems(new ArrayList<Item>());
    }
    Assert.assertFalse(director.getInventory() == null);
    director.getInventory().acquireItem(potion);
    Assert.assertNotNull(director.getInventory().getItemById(potion.getId()));
    // Okay, this potion is in the inventory, let's use it.
    Assert.assertTrue(director.getInventory().useItem(potion));
    // Okay, we used the item, we shouldn't have it anymore.
    Assert.assertFalse(director.getInventory().useItem(potion));
    // Okay, let's get the item back again.
    director.getInventory().acquireItem(potion);
    Assert.assertNotNull(director.getInventory().getItemById(potion.getId()));
    Assert.assertTrue(potion.isPossessedByPlayer());
    Assert.assertTrue(potion.getCount() == 1);
    
  }
  
  @Test
  public void testCreateWeapon()
  {
    testCreateDirector();
    WeaponItem sword = new WeaponItem(director);
    Assert.assertNotNull(sword);
    sword.setWeaponClass(WeaponItem.WEAPON_CLASS_SWORD);
    Assert.assertEquals(WeaponItem.WEAPON_CLASS_SWORD,sword.getWeaponClass());
    sword.setCount(1);
    Assert.assertEquals(1,sword.getCount());
    sword.setId(2);
    Assert.assertEquals(2,sword.getId());
    sword.setName("Broadsword");
    Assert.assertEquals("Broadsword", sword.getName());
    sword.setPossessedByPlayer(true);
    Assert.assertEquals(true, sword.isPossessedByPlayer());
    sword.setSlot(WeaponItem.WEAPON_SLOT_MAIN_HAND);
    Assert.assertEquals(WeaponItem.WEAPON_SLOT_MAIN_HAND, sword.getSlot());
    Assert.assertFalse(sword.isConsumable());
    Assert.assertFalse(sword.isUsable());
    
    Stat stat = new Stat(director);
    stat.setName("Agility");
    stat.setValue(25d);
    sword.getEquipmentStatSheet().getStats().put(Stat.STAT_SLOT_AGILITY, stat);

    Assert.assertTrue(25d == sword.getStat(Stat.STAT_SLOT_AGILITY));
    
    testCreateInventory();
    director.getInventory().acquireItem(sword);
    Assert.assertTrue(director.getInventory().getItems().size() > 0);
    
    boolean exists = false;
    for(Item item: director.getInventory().getItems())
    {
      if(item.equals(sword))
      {
        exists = true;
      }
    }
    Assert.assertTrue(exists);
  }
  
  @Test
  public void testCreateOHWeapon()
  {
    testCreateDirector();
    WeaponItem sword = new WeaponItem(director);
    Assert.assertNotNull(sword);
    sword.setWeaponClass(WeaponItem.WEAPON_CLASS_SWORD);
    Assert.assertEquals(WeaponItem.WEAPON_CLASS_SWORD,sword.getWeaponClass());
    sword.setCount(1);
    Assert.assertEquals(1,sword.getCount());
    sword.setId(4);
    Assert.assertEquals(4,sword.getId());
    sword.setName("Broadsword");
    Assert.assertEquals("Broadsword", sword.getName());
    sword.setPossessedByPlayer(true);
    Assert.assertEquals(true, sword.isPossessedByPlayer());
    sword.setSlot(WeaponItem.WEAPON_SLOT_OFF_HAND);
    Assert.assertEquals(WeaponItem.WEAPON_SLOT_OFF_HAND, sword.getSlot());
    Assert.assertFalse(sword.isConsumable());
    Assert.assertFalse(sword.isUsable());
    
    Stat stat = new Stat(director);
    stat.setName("Agility");
    stat.setValue(25d);
    sword.getEquipmentStatSheet().getStats().put(Stat.STAT_SLOT_AGILITY, stat);

    Assert.assertEquals(25d, sword.getStat(Stat.STAT_SLOT_AGILITY));
    
    testCreateInventory();
    director.getInventory().acquireItem(sword);
    Assert.assertTrue(director.getInventory().getItems().size() > 0);
    
    boolean exists = false;
    for(Item item: director.getInventory().getItems())
    {
      if(item.equals(sword))
      {
        exists = true;
      }
    }
    Assert.assertTrue(exists);
  }
    
  @Test
  public void testCreateArmor()
  {
    testCreateDirector();
    ArmorItem chestPiece = new ArmorItem(director);
    Assert.assertNotNull(chestPiece);
    chestPiece.setArmorClass(ArmorItem.ARMOR_CLASS_LEATHER);
    Assert.assertEquals(ArmorItem.ARMOR_CLASS_LEATHER,chestPiece.getArmorClass());
    chestPiece.setSlot(ArmorItem.EQUIPMENT_SLOT_CHEST);
    Assert.assertEquals(ArmorItem.EQUIPMENT_SLOT_CHEST, chestPiece.getSlot());
    chestPiece.setCount(1);
    Assert.assertEquals(1,chestPiece.getCount());
    chestPiece.setId(3);
    Assert.assertEquals(3,chestPiece.getId());
    chestPiece.setName("Breastplate");
    Assert.assertEquals("Breastplate", chestPiece.getName());
    chestPiece.setPossessedByPlayer(true);
    Assert.assertEquals(true, chestPiece.isPossessedByPlayer());
    Assert.assertFalse(chestPiece.isConsumable());
    Assert.assertFalse(chestPiece.isUsable());
    
    testCreateInventory();
    director.getInventory().acquireItem(chestPiece);
    Assert.assertTrue(director.getInventory().getItems().size() > 0);
    
    boolean exists = false;
    for(Item item: director.getInventory().getItems())
    {
      if(item.equals(chestPiece))
      {
        exists = true;
      }
    }
    Assert.assertTrue(exists);
  }
  
  @Test
  public void testCreatePartyCharacter()
  {
    testCreateDirector();
    Character jimmy = new Character(director);
    Assert.assertNotNull(jimmy);
    jimmy.setName("Jimmy");
    Assert.assertEquals("Jimmy", jimmy.getName());
    jimmy.setId(1);
    Assert.assertEquals(1,jimmy.getId());
    jimmy.setArmorClass(ArmorItem.ARMOR_CLASS_CLOTH);
    Assert.assertEquals(ArmorItem.ARMOR_CLASS_CLOTH, jimmy.getArmorClass());
    jimmy.setWeaponClass(WeaponItem.WEAPON_CLASS_AXE);
    Assert.assertEquals(WeaponItem.WEAPON_CLASS_AXE, jimmy.getWeaponClass());
    
    HashMap<Integer,Stat> stats = new HashMap<Integer,Stat>();
    Stat stat = new Stat(director);
    stat.setName("Agility");
    stat.setValue(50d);
    stats.put(Stat.STAT_SLOT_AGILITY, stat);
    jimmy.getBaseCharacterSheet().setStats(stats);
    
    Assert.assertTrue(50d == jimmy.getBaseCharacterSheet().getStats().get(Stat.STAT_SLOT_AGILITY).getValue());
    
    testCreateCharacterController();
    
    ArrayList<Character> party = new ArrayList<Character>();
    party.add(jimmy);
    director.getCharacterController().setPartyMembers(party);
    Assert.assertNotNull(director.getCharacterController().getPartyMembers().get(0));
    Assert.assertEquals("Jimmy",director.getCharacterController().getPartyMembers().get(0).getName());
    Assert.assertEquals(1,director.getCharacterController().getPartyMembers().get(0).getId());
    Assert.assertEquals(ArmorItem.ARMOR_CLASS_CLOTH,director.getCharacterController().getPartyMembers().get(0).getArmorClass());
    Assert.assertEquals(WeaponItem.WEAPON_CLASS_AXE,director.getCharacterController().getPartyMembers().get(0).getWeaponClass());
  }
  
  @Test
  public void testCreateOpponentCharacter()
  {
    testCreateDirector();
    Character eviljimmy = new Character(director);
    Assert.assertNotNull(eviljimmy);
    eviljimmy.setName("EvilJimmy");
    Assert.assertEquals("EvilJimmy", eviljimmy.getName());
    eviljimmy.setId(1);
    Assert.assertEquals(1,eviljimmy.getId());
    eviljimmy.setArmorClass(ArmorItem.ARMOR_CLASS_CLOTH);
    Assert.assertEquals(ArmorItem.ARMOR_CLASS_CLOTH, eviljimmy.getArmorClass());
    eviljimmy.setWeaponClass(WeaponItem.WEAPON_CLASS_AXE);
    Assert.assertEquals(WeaponItem.WEAPON_CLASS_AXE, eviljimmy.getWeaponClass());
    
    testCreateCharacterController();
    
    ArrayList<Character> opponents = new ArrayList<Character>();
    opponents.add(eviljimmy);
    director.getCharacterController().setOpponents(opponents);
    Assert.assertNotNull(director.getCharacterController().getOpponents().get(0));
    Assert.assertEquals("EvilJimmy",director.getCharacterController().getOpponents().get(0).getName());
    Assert.assertEquals(1,director.getCharacterController().getOpponents().get(0).getId());
    Assert.assertEquals(ArmorItem.ARMOR_CLASS_CLOTH,director.getCharacterController().getOpponents().get(0).getArmorClass());
    Assert.assertEquals(WeaponItem.WEAPON_CLASS_AXE,director.getCharacterController().getOpponents().get(0).getWeaponClass());
  }
  
  @Test
  public void testPartyVersusOpponents()
  {
    testCreatePartyCharacter();
    testCreateOpponentCharacter();

    Assert.assertNotNull(director.getCharacterController().getPartyMembers().get(0));
    Assert.assertEquals("Jimmy",director.getCharacterController().getPartyMembers().get(0).getName());
    Assert.assertEquals(1,director.getCharacterController().getPartyMembers().get(0).getId());
    Assert.assertEquals(ArmorItem.ARMOR_CLASS_CLOTH,director.getCharacterController().getPartyMembers().get(0).getArmorClass());
    Assert.assertEquals(WeaponItem.WEAPON_CLASS_AXE,director.getCharacterController().getPartyMembers().get(0).getWeaponClass());

    Assert.assertNotNull(director.getCharacterController().getOpponents().get(0));
    Assert.assertEquals("EvilJimmy",director.getCharacterController().getOpponents().get(0).getName());
    Assert.assertEquals(1,director.getCharacterController().getOpponents().get(0).getId());
    Assert.assertEquals(ArmorItem.ARMOR_CLASS_CLOTH,director.getCharacterController().getOpponents().get(0).getArmorClass());
    Assert.assertEquals(WeaponItem.WEAPON_CLASS_AXE,director.getCharacterController().getOpponents().get(0).getWeaponClass());
  }

  @Test
  public void testInventory()
  {
    testCreateDirector();
    
    new Inventory(director);
    director.getInventory().setItems(new ArrayList<Item>());
    
    testCreateArmor();
    testCreateWeapon();
    
    Assert.assertTrue(director.getInventory().getItems().size() == 2);
    for(Item item: director.getInventory().getItems())
    {
      Assert.assertTrue(item.getCount() == 1);
      director.getInventory().acquireItem(item);
    }
    
    for(Item item: director.getInventory().getItems())
    {
      Assert.assertTrue(item.getCount() == 2);
      director.getInventory().loseItem(item);
    }

    for(Item item: director.getInventory().getItems())
    {
      Assert.assertTrue(item.getCount() == 1);
      director.getInventory().loseItem(item);
    }

    for(Item item: director.getInventory().getItems())
    {
      Assert.assertTrue(item.getCount() == 0);
      director.getInventory().loseItem(item);
      Assert.assertTrue(item.getCount() == 0);
    }

    Assert.assertTrue(director.getInventory().getItems().size() == 2);
  }
  
  @Test
  public void testEquipping()
  {
    testCreateDirector();
    
    new Inventory(director);
    director.getInventory().setItems(new ArrayList<Item>());
    
    testPartyVersusOpponents();
    testCreateArmor();
    testCreateWeapon();
    
    Assert.assertNotNull(director.getCharacterController().getPartyMembers().get(0));
    
    Character jimmy = director.getCharacterController().getPartyMembers().get(0);
    
    Assert.assertTrue(50d == jimmy.getStat(Stat.STAT_SLOT_AGILITY).getValue());

    Assert.assertTrue(director.getInventory().getItems().size() == 2);
    
    List<WeaponItem> weapons = director.getInventory().getWeaponItems();
    
    Assert.assertTrue(weapons.size() == 1);
    
    jimmy.equipWeapon(weapons.get(0));
    
    // Can't equip the item, shouldn't let us.
    Assert.assertFalse(weapons.get(0).getCount() == 0);
    Assert.assertTrue(weapons.get(0).isPossessedByPlayer());
    Assert.assertTrue(50d == jimmy.getStat(Stat.STAT_SLOT_AGILITY).getValue());
    
    jimmy.setWeaponClass(WeaponItem.WEAPON_CLASS_SWORD);
    
    jimmy.equipWeapon(weapons.get(0));
    
    Assert.assertTrue(25d == jimmy.getEquipmentStatSheet().getStats().get(Stat.STAT_SLOT_AGILITY).getValue());
    Assert.assertTrue(50d == jimmy.getBaseCharacterSheet().getStats().get(Stat.STAT_SLOT_AGILITY).getValue());
    Assert.assertTrue(75d == jimmy.getStats().get(Stat.STAT_SLOT_AGILITY).getValue());
    
    // Can equip the item now, should be count == 0
    Assert.assertTrue(weapons.get(0).getCount() == 0);
    Assert.assertFalse(weapons.get(0).isPossessedByPlayer());
    Assert.assertEquals(jimmy.getWeapons().get(WeaponItem.WEAPON_SLOT_MAIN_HAND),weapons.get(0));

    WeaponItem sword = new WeaponItem(director);
    sword.setWeaponClass(WeaponItem.WEAPON_CLASS_SWORD);
    sword.setCount(1);
    sword.setId(4);
    sword.setName("Smallsword");
    sword.setPossessedByPlayer(true);
    sword.setSlot(WeaponItem.WEAPON_SLOT_OFF_HAND);
    
    Stat stat = new Stat(director);
    stat.setName("Agility");
    stat.setValue(25d);
    sword.getEquipmentStatSheet().getStats().put(Stat.STAT_SLOT_AGILITY, stat);
    
    director.getInventory().acquireItem(sword);
    
    Assert.assertTrue(director.getInventory().getWeaponItems().get(1).isPossessedByPlayer());
    Assert.assertTrue(jimmy.getWeapons().get(WeaponItem.WEAPON_SLOT_MAIN_HAND).equals(weapons.get(0)));
    
    jimmy.equipWeapon(sword);

    //System.err.println(jimmy.getBaseCharacterSheet().getStats().get(Stat.STAT_SLOT_AGILITY).getValue());
    //System.err.println(jimmy.getEquipmentStatSheet().getStat(Stat.STAT_SLOT_AGILITY));
    Assert.assertTrue(100d == jimmy.getStats().get(Stat.STAT_SLOT_AGILITY).getValue());
  }
  
  @Test
  public void testUnequipping()
  {
    testCreateDirector();
    
    new Inventory(director);
    director.getInventory().setItems(new ArrayList<Item>());
    
    testPartyVersusOpponents();
    testCreateArmor();
    testCreateWeapon();
    
    Assert.assertNotNull(director.getCharacterController().getPartyMembers().get(0));
    
    Character jimmy = director.getCharacterController().getPartyMembers().get(0);
    
    jimmy.setWeaponClass(WeaponItem.WEAPON_CLASS_SWORD);

    Assert.assertTrue(director.getInventory().getItems().size() == 2);
    
    List<WeaponItem> weapons = director.getInventory().getWeaponItems();
    
    Assert.assertNull(jimmy.getWeapons().get(WeaponItem.WEAPON_SLOT_MAIN_HAND));
    Assert.assertTrue(weapons.get(0).getCount() == 1);
    Assert.assertTrue(weapons.get(0).isPossessedByPlayer());
    Assert.assertEquals(WeaponItem.WEAPON_SLOT_MAIN_HAND,weapons.get(0).getSlot());

    jimmy.equipWeapon(weapons.get(0));
    
    Assert.assertTrue(75d == jimmy.getStat(Stat.STAT_SLOT_AGILITY).getValue());

    Assert.assertNotNull(jimmy.getWeapons().get(WeaponItem.WEAPON_SLOT_MAIN_HAND));
    Assert.assertTrue(weapons.get(0).getCount() == 0);
    Assert.assertFalse(weapons.get(0).isPossessedByPlayer());
    
    jimmy.unequipWeapon(WeaponItem.WEAPON_SLOT_MAIN_HAND);
    
    Assert.assertTrue(50d == jimmy.getStat(Stat.STAT_SLOT_AGILITY).getValue());
    
    // Double unequip shouldn't do anything OR fail... just... do nothing.
    jimmy.unequipWeapon(WeaponItem.WEAPON_SLOT_MAIN_HAND);
    
    Assert.assertTrue(50d == jimmy.getStat(Stat.STAT_SLOT_AGILITY).getValue());
    
    Assert.assertNull(jimmy.getWeapons().get(WeaponItem.WEAPON_SLOT_MAIN_HAND));
  }
  
  @Test
  public void testStatusEffect()
  {
    testCreateDirector();
    testCreateInventory();
    testPartyVersusOpponents();
    
    Assert.assertNotNull(director.getCharacterController().getPartyMembers().get(0));
    
    Character jimmy = director.getCharacterController().getPartyMembers().get(0);
    
    jimmy.setWeaponClass(WeaponItem.WEAPON_CLASS_SWORD);
    
    WeaponItem sword = new WeaponItem(director);
    sword.setWeaponClass(WeaponItem.WEAPON_CLASS_SWORD);
    sword.setCount(1);
    sword.setId(4);
    sword.setName("Smallsword");
    sword.setPossessedByPlayer(true);
    sword.setSlot(WeaponItem.WEAPON_SLOT_OFF_HAND);
    
    Stat stat = new Stat(director);
    stat.setName("Agility");
    stat.setValue(25d);
    sword.getEquipmentStatSheet().getStats().put(Stat.STAT_SLOT_AGILITY, stat);
    
    director.getInventory().acquireItem(sword);
    
    jimmy.equipWeapon(sword);
    
    Assert.assertTrue(75d == jimmy.getStats().get(Stat.STAT_SLOT_AGILITY).getValue());
    
    StatusEffectStatSheet poison = new StatusEffectStatSheet(director);
    HashMap<Integer,Stat> poisonStats = new HashMap<Integer,Stat>();
    Stat pagi = new Stat(director);
    pagi.setName("Agility");
    pagi.setValue(-50d);
    poisonStats.put(Stat.STAT_SLOT_AGILITY, pagi);
    poison.setStats(poisonStats);
    
    jimmy.getCharacterEffectStatSheet().applyStatus(poison);
    
    Assert.assertTrue(25d == jimmy.getStats().get(Stat.STAT_SLOT_AGILITY).getValue());
  }
  
  /**
   * @throws java.lang.Exception
   */
  @Before
  public void setUp() throws Exception
  {
  }

  /**
   * @throws java.lang.Exception
   */
  @After
  public void tearDown() throws Exception
  {
  }
}
